/*
 * $Id: GamePlan.java 24 2009-03-31 23:40:10Z aaron.day $
 */
package org.crazydays.gameplan;


import java.awt.Point;
import java.beans.PropertyVetoException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRootPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.event.InternalFrameListener;
import javax.swing.event.TreeSelectionListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.crazydays.gameplan.data.Database;
import org.crazydays.gameplan.data.swing.tree.DatabaseTreeSelectionListenerDelegate;
import org.crazydays.gameplan.data.swing.tree.JDatabaseTreePane;
import org.crazydays.gameplan.map.IGameMap;
import org.crazydays.gameplan.map.swing.JMapFrame;
import org.crazydays.gameplan.swing.ExitAction;
import org.crazydays.gameplan.swing.InternalFrameListenerDelegate;

/**
 * GamePlan is the root class for the program. It should be usable within either
 * a JFrame or JApplet.
 */
public class GamePlan
        extends JRootPane
{
	/** serial version uid */
	public final static long serialVersionUID =
	        "$Id: GamePlan.java 24 2009-03-31 23:40:10Z aaron.day $"
	                .hashCode();

	/** log */
	protected static Log LOG = LogFactory.getLog(GamePlan.class);

	/** database */
	protected Database database;

	/** actions */
	protected Actions actions;

	/** internal frame listener delegate */
	protected InternalFrameListener internalFrameListener =
	        new InternalFrameListenerDelegate(this);

	/** tree selection listener delegate */
	protected TreeSelectionListener treeSelectionListener =
	        new DatabaseTreeSelectionListenerDelegate(this);

	/** menu bar */
	protected JMenuBar menuBar;

	/** split pane */
	protected JSplitPane splitPane;

	/** tabbed pane */
	protected JTabbedPane tabbedPane;

	/** tree pane */
	protected JDatabaseTreePane treePane;

	/** desktop pane */
	protected JDesktopPane desktopPane;

	/** open maps */
	protected Map<IGameMap, JMapFrame> openMaps =
	        new HashMap<IGameMap, JMapFrame>();

	/**
	 * GamePlan constructor.
	 */
	public GamePlan()
	{
		initialize();
	}

	/**
	 * Initialize GamePlan.
	 */
	protected void initialize()
	{
		initializeActions();
		initializeDialogs();
		initializeMenuBar();
		initializeSplitPane();

		initializeDatabase();
	}

	/**
	 * Initialize actions.
	 */
	protected void initializeActions()
	{
		this.actions = new Actions(this);

	}

	/**
	 * Initialize dialogs.
	 */
	protected void initializeDialogs()
	{
	}

	/**
	 * Initialize menu bar.
	 */
	protected void initializeMenuBar()
	{
		// initialize menu
		menuBar = new JMenuBar();

		// file menu
		initializeFileMenu();

		// edit menu
		JMenu editMenu = new JMenu("Edit");
		menuBar.add(editMenu);

		// view menu
		initializeViewMenu();

		// help menu
		JMenu helpMenu = new JMenu("Help");
		menuBar.add(helpMenu);

		JMenuItem about = new JMenuItem(actions.get(Actions.ABOUT));
		helpMenu.add(about);

		setJMenuBar(menuBar);
	}

	/**
	 * Initialize file menu.
	 */
	protected void initializeFileMenu()
	{
		// file menu
		JMenu fileMenu = new JMenu("File");
		menuBar.add(fileMenu);

		JMenuItem newDatabase = new JMenuItem("New Database");
		fileMenu.add(newDatabase);

		JMenuItem openDatabase =
		        new JMenuItem(actions.get(Actions.OPEN_DATABASE));
		fileMenu.add(openDatabase);

		fileMenu.addSeparator();

		JMenuItem save = new JMenuItem(actions.get(Actions.SAVE));
		fileMenu.add(save);

		JMenuItem saveAs = new JMenuItem(actions.get(Actions.SAVE_AS));
		fileMenu.add(saveAs);

		fileMenu.addSeparator();

		JMenuItem newMap =
		        new JMenuItem(actions.get(Actions.OPEN_CREATE_MAP));
		fileMenu.add(newMap);

		fileMenu.addSeparator();

		JMenuItem exit = new JMenuItem(new ExitAction(this));
		fileMenu.add(exit);
	}

	/**
	 * Initialize view menu.
	 */
	protected void initializeViewMenu()
	{
		// file menu
		JMenu viewMenu = new JMenu("View");
		menuBar.add(viewMenu);

		JMenuItem grid =
		        new JMenuItem(actions.get(Actions.GRID_TOGGLE));
		viewMenu.add(grid);
	}

	/**
	 * Initialize split pane.
	 */
	protected void initializeSplitPane()
	{
		initializeTabbedPane();
		initializeDesktopPane();

		// split pane
		splitPane =
		        new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
		                tabbedPane, desktopPane);
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(150);

		setContentPane(splitPane);
	}

	/**
	 * Initialize desktop pane.
	 */
	protected void initializeDesktopPane()
	{
		// desktop pane
		desktopPane = new JDesktopPane();
	}

	/**
	 * Initialize tabbed pane.
	 */
	protected void initializeTabbedPane()
	{
		// tabbed menu pane
		tabbedPane =
		        new JTabbedPane(JTabbedPane.TOP,
		                JTabbedPane.WRAP_TAB_LAYOUT);
	}

	/**
	 * Initialize database.
	 */
	protected void initializeDatabase()
	{
		openDatabase(new Database());
	}

	/**
	 * Get database.
	 * 
	 * @return Database
	 */
	public Database getDatabase()
	{
		return database;
	}

	/**
	 * Set database. Only the this class ought to be setting the database.
	 * Call openDatabase(Database).
	 * 
	 * @param database Database
	 */
	protected void setDatabase(Database database)
	{
		this.database = database;
	}

	/**
	 * Open database and initialize the database tree pane.
	 * 
	 * @param database Database
	 */
	public void openDatabase(Database database)
	{
		closeDatabase();

		setDatabase(database);

		// add new database tree pane
		treePane = new JDatabaseTreePane(getDatabase());
		treePane.addTreeSelectionListener(treeSelectionListener);

		tabbedPane.add("Database", treePane);

		// open game maps
		for (IGameMap gameMap : getDatabase().getGameMaps()) {
			openGameMap(gameMap);
		}
	}

	/**
	 * Close current database.
	 */
	protected void closeDatabase()
	{
		// TODO: add save prompt

		// close all open frames
		for (JInternalFrame internalFrame : desktopPane.getAllFrames()) {
			try {
				internalFrame.setClosed(true);
			} catch (PropertyVetoException e) {
				LOG.warn("Unable to close internalFrame: "
				        + internalFrame, e);
			}
		}

		// all open tabs
		tabbedPane.removeAll();
	}

	/**
	 * Get actions.
	 * 
	 * @return Actions
	 */
	public Actions getActions()
	{
		return actions;
	}

	/**
	 * Get active internal frame.
	 * 
	 * @return InternalFrame
	 */
	public JInternalFrame getActiveInternalFrame()
	{
		return desktopPane.getSelectedFrame();
	}

	/**
	 * Get game map.
	 * 
	 * @param name Name
	 * @return GameMap
	 */
	public IGameMap getGameMap(String name)
	{
		if (name == null) {
			throw new IllegalArgumentException("name == null");
		}

		for (IGameMap gameMap : getDatabase().getGameMaps()) {
			if (name.equals(gameMap.getName())) {
				return gameMap;
			}
		}

		return null;
	}

	/**
	 * Open game map.
	 * 
	 * @param gameMap Game map
	 */
	public void openGameMap(IGameMap gameMap)
	{
		// add map to database if it is
		// not already in the database
		if (!getDatabase().getGameMaps().contains(gameMap)) {
			getDatabase().addGameMap(gameMap);
		}

		// open map frame
		if (openMaps.containsKey(gameMap)) {
			// move frame to front
			JMapFrame mapFrame = openMaps.get(gameMap);
			desktopPane.moveToFront(mapFrame);
		} else {
			// open new map display
			int offset = openMaps.size() * 10;

			JMapFrame mapFrame =
			        new JMapFrame(this, gameMap, new Point(offset,
			                offset));

			// register frame with delegate
			mapFrame
			        .addInternalFrameListener(internalFrameListener);

			desktopPane.add(mapFrame);
			openMaps.put(gameMap, mapFrame);

			treePane.addGameMap(gameMap);

			desktopPane.moveToFront(mapFrame);
		}
	}
}
